package com.crazymakercircle.dhptester.handler;

import com.alibaba.fastjson.JSONObject;
import com.crazymakercircle.constants.ServerConstants;
import com.crazymakercircle.dhptester.benchmark.HttpBenchmark;
import com.crazymakercircle.zk.SnowflakeIdGenerator;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.AttributeKey;
import io.netty.util.CharsetUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.URI;
import java.util.Random;

@Sharable
public class DhpTesterHandler extends ChannelInboundHandlerAdapter {
    private String host;
    private int port;
    private long end_time;
    private int print_res;
    private JSONObject jsondata;

    private String method;
    HttpBenchmark httpBenchmark = null;

    private AttributeKey<Long> start_time = AttributeKey.valueOf("start_time" + new Random().nextInt());
    private AttributeKey<JSONObject> expect_data = AttributeKey.valueOf("expect_data" + new Random().nextInt());
    JSONObject expect = null;
    private Logger logger;

    public DhpTesterHandler(String host, int port, long end_time, JSONObject jsondata, HttpBenchmark httpBenchmark) {
        this.host = host;
        this.port = port;
        this.end_time = end_time;
        this.jsondata = jsondata;

        this.method = this.jsondata.getString("method");
        this.expect = this.jsondata.getJSONObject("expect");

        this.httpBenchmark = httpBenchmark;

        this.logger = LogManager.getLogger(DhpTesterHandler.class.getName());
    }

    public void sendRequest(ChannelHandlerContext ctx) throws Exception {
        DefaultFullHttpRequest request;
        long user_id = SnowflakeIdGenerator.getSingleton().nextId();

        URI uri = new URI(this.jsondata.getString("url"));
        if (this.method.equals("POST")) {
            String content = this.jsondata.getJSONObject("body").toString();

            if (content.indexOf(ServerConstants.SNOW_FLAKE_ID) > 0) {
                content = content.replaceAll(ServerConstants.SNOW_FLAKE_ID, String.valueOf(user_id));
            }

            request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1,
                    HttpMethod.POST, uri.toASCIIString(), Unpooled.wrappedBuffer(content.getBytes()));
        } else if (this.method.equals("GET")) {
            request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri.toASCIIString());
        } else {
            System.out.println("ERROR : not supported http method");
            ctx.close();
            return;
        }

        request.headers().set(HttpHeaderNames.HOST, this.host);
        request.headers().set(HttpHeaderNames.CONTENT_TYPE, "application/json;charset=utf-8");
        request.headers().set(HttpHeaderNames.CONTENT_LENGTH, request.content().readableBytes());
        request.headers().add(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        request.headers().add("USER-ID", user_id);
        ctx.attr(start_time).set(System.currentTimeMillis());
        ctx.attr(expect_data).set(expect);
        //long st = (Long)ctx.attr(start_time).get();
        //System.out.println("start time : " + st);
        ctx.write(request);
        ctx.flush();

        httpBenchmark.total_req.add(1);
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        //System.out.println("client active ...");
        this.sendRequest(ctx);
        //System.out.println("client write request ...");
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        if (msg instanceof FullHttpResponse) {
            FullHttpResponse response = (FullHttpResponse) msg;
            //System.out.println("CONTENT_TYPE:" + response.headers().get(HttpHeaders.Names.CONTENT_TYPE));

//            ByteBuf buf = response.content();
//            String result = buf.toString(CharsetUtil.UTF_8);
//            System.out.println("response -> "+ result);
            if (!response.status().equals(HttpResponseStatus.OK)) {
                httpBenchmark.total_err.add(1);


                ByteBuf buf = response.content();
                String result = buf.toString(CharsetUtil.UTF_8);
                System.out.println("response -> " + result);
            } else {
                httpBenchmark.total_req_res.add(1);
//                ByteBuf buf = response.content();
//                String result = buf.toString(CharsetUtil.UTF_8);
//                System.out.println("response -> " + result);
            }
            ((FullHttpResponse) msg).release();
            long et = System.currentTimeMillis();

            long st = (Long) ctx.attr(start_time).get();

            long elapsed = et - st;

            httpBenchmark.total_res_time.add(elapsed);
            if (elapsed < 10) {
                httpBenchmark.below_10.add(1);
            } else if (elapsed >= 10 && elapsed < 20) {
                httpBenchmark.between_10_20.add(1);
            } else if (elapsed >= 20 && elapsed < 30) {
                httpBenchmark.between_20_30.add(1);
            } else {
                httpBenchmark.over_30.add(1);
            }

            if (!httpBenchmark.isOver) {
                this.sendRequest(ctx);
            } else {
                ctx.close();
            }
        }


    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        httpBenchmark.total_err.add(1);
        logger.error(cause.toString());

        //ctx.close();
    }
}
